﻿using System;
using System.Collections;
using System.Collections.Generic;                                             
using System.Linq;
using System.Text;
using System.Reflection;
using System.Data;
using GFrame.Data;

namespace GFrame.Shadow
{

    public enum FieldTypeEnum { PK, FK, Normal, PFK }

    [AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
    public class GField : Attribute
    {
        private bool _seq;

        private string _strFieldName;
        private FieldTypeEnum _FieldType = FieldTypeEnum.Normal;

        public GField(string Field)
        {
            this._strFieldName = Field;
        }

        public GField(string Field, bool Sequence)
        {
            this._strFieldName = Field;
            this._FieldType = FieldTypeEnum.Normal;

            this._seq = Sequence;
        }

        public GField(string Field, FieldTypeEnum FieldType, bool Sequence)
        {
            this._strFieldName = Field;
            this._FieldType = FieldType;

            this._seq = Sequence;
        }

        public GField(string Field, FieldTypeEnum FieldType)
        {
            this._strFieldName = Field;
            this._FieldType = FieldType;
            this._seq = false;
        }

        public FieldTypeEnum FieldType
        {
            get { return _FieldType; }
            set { _FieldType = value; }
        }

        public string FieldName
        {
            get { return this._strFieldName; }
            set { this._strFieldName = value; }
        }

        public bool Sequence
        {
            get { return this._seq; }
            set { this._seq = value; }
        }
    }

    [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
    public class GTable : Attribute
    {

        private string _strTableName;

        public GTable(string Table)
        {
            this._strTableName = Table;
        }

        public string TableName
        {
            get { return this._strTableName; }
            set { this._strTableName = value; }
        }
    }


    /// <summary>
    /// GFrame ORM module
    /// Jay.Gu
    /// 2010-6-8
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class Shadow<T>: IShadow<T>, IDisposable
    {
        private T _obj;

        //command string builder

        private StringBuilder _sbSelectSQL = new StringBuilder();
        private StringBuilder _sbUpdateSQL = new StringBuilder();
        private StringBuilder _sbInsertSQL = new StringBuilder();
        private StringBuilder _sbDeleteSQL = new StringBuilder();

        private string _strTable;

        private string _strWhereSQL;

        private string _strErrorMessage;

        //params
        private ParamList _plKey = new ParamList();
        private ParamList _pl = new ParamList();
        private ParamList _plInsert = new ParamList();

        //rule manager
        private ShadowRule _rule = new ShadowRule();

        //db object
        private SQLReader _sr;
        private SQLWriter _sw;

        private Dictionary<string, object> _dicKeyValues;

        public Shadow(T obj)
        {
            _obj = obj;

            _sr = new SQLReader();
            _sw = new SQLWriter();


            Type t = _obj.GetType();

            _strTable = (t.GetCustomAttributes(typeof(GTable), false)[0] as GTable).TableName;

            if (!string.IsNullOrEmpty(_strTable))
                _CreateKeyValuesTable(t);
            else
            {
                throw new Exception("Table name not found!!");
            }

            //CreateSQLCommand();
        }


        public void Dispose()
        {
            if (_sr != null) _sr.Dispose();
            if (_sw != null) _sw.Dispose();

            _dicKeyValues.Clear();
            _dicKeyValues = null;

            _plKey.Dispose();
            _pl.Dispose();
            _plInsert.Dispose();

            _sbSelectSQL = null;
            _sbUpdateSQL = null;
            _sbInsertSQL = null;
            _sbDeleteSQL = null;
        }

        /// <summary>
        /// create old key table
        /// </summary>
        /// <param name="type"></param>
        private void _CreateKeyValuesTable(Type type)
        {
            if (_dicKeyValues == null)
            {
                _dicKeyValues = new Dictionary<string, object>();

                foreach (PropertyInfo pi in type.GetProperties())
                {
                    GField field = (pi.GetCustomAttributes(typeof(GField), false)[0] as GField);

                    if (field != null)
                    {
                        if (field.FieldType == FieldTypeEnum.PK)
                        {
                            object obj = pi.GetValue(_obj, null);

                            if(obj != null)
                                _dicKeyValues.Add(pi.Name, obj);

                        }
                    }
                }
            }
        }

        private void _RewriteKeyValues(ParamList pl)
        {
            if (_dicKeyValues != null)
            {
                Type t = _obj.GetType();

                foreach (PropertyInfo pi in t.GetProperties())
                {
                    GField field = (pi.GetCustomAttributes(typeof(GField), false)[0] as GField);

                    if (field != null)
                    {
                        if (field.FieldType == FieldTypeEnum.PK)
                        {
                            if (_dicKeyValues.ContainsKey(pi.Name))
                            {
                                if (!field.Sequence)
                                    _dicKeyValues[pi.Name] = pi.GetValue(_obj, null);
                                else
                                    _dicKeyValues[pi.Name] = _getValue(pi, pl);
                            }
                        }

                        //更新序列值
                        if (field.Sequence)
                            _setValue(pi, pl);
                    }
                }

            }
        }

        private object _getValue(PropertyInfo pi, ParamList pl)
        {
            foreach (DataParam dp in pl)
            {
                if (dp.ParamName == "@key_" + pi.Name || dp.ParamName == "@" + pi.Name)
                {
                    return dp.Value;
                }
            }

            return null;
        }

        private void _setValue(PropertyInfo pi, ParamList pl)
        {
            //rewrite value...
            foreach (DataParam dp in pl)
            {
                if (dp.ParamName == "@key_" + pi.Name || dp.ParamName == "@" + pi.Name)
                {
                    if (dp.Value != DBNull.Value)
                    {
                        pi.SetValue(_obj, dp.Value, null);

                        break;
                    }
                }
            }
        }

        private string _strConnectStringName = "";

        /// <summary>
        /// Config文件中的链接字符串名称
        /// </summary>
        public String ConnectionName
        {
            set
            {
                _strConnectStringName = value;
            }

            get
            {
                return _strConnectStringName;
            }
        }

        public String ErrorMessage
        {
            get
            {
                return _strErrorMessage;
            }
        }

        /// <summary>
        /// 生成SQL语句
        /// </summary>
        private void CreateSQLCommand()
        {

            _strErrorMessage = string.Empty;

            _pl.Clear();
            _plKey.Clear();
            _plInsert.Clear();

            _sbInsertSQL.Remove(0, _sbInsertSQL.Length);
            _sbUpdateSQL.Remove(0, _sbUpdateSQL.Length);
            _sbDeleteSQL.Remove(0, _sbDeleteSQL.Length);
            _sbSelectSQL.Remove(0, _sbSelectSQL.Length);

            Type t = _obj.GetType();

            _sbInsertSQL.Append("INSERT INTO " + _strTable + "(");
            _sbUpdateSQL.Append("UPDATE " + _strTable + " SET ");
            _sbDeleteSQL.Append("DELETE FROM " + _strTable);

            string strInsertCols = string.Empty;

            string strInsertValues = " VALUES(";

            string strUpdateCols = string.Empty;

            string strSet = "";

            foreach (PropertyInfo pi in t.GetProperties())
            {
                GField field = (pi.GetCustomAttributes(typeof(GField), false)[0] as GField);

                if (field != null)
                {
                    
                    bool b = AddToParamsList(pi, field);
                    
                    if (b)
                    {
                        if (!field.Sequence)
                        {
                            //Insert
                            strInsertCols += field.FieldName + ",";
                            strInsertValues += "@" + pi.Name + ",";


                        }
                        else
                        {
                            strSet = field.FieldName;
                        }
                    }

                    //update & delete
                    if (field.FieldType == FieldTypeEnum.PK)
                    {
                        if (b)
                        {
                            if (string.IsNullOrEmpty(_strWhereSQL))
                                _strWhereSQL = " WHERE " + field.FieldName + "=@key_" + pi.Name;
                            else
                                _strWhereSQL += " AND " + field.FieldName + "=@key_" + pi.Name;
                        }
                        else
                        {
                            throw new Exception("PK is NULL!");
                        }
                    }

                    if (b)
                        if(!field.Sequence) strUpdateCols += field.FieldName + "=@" + pi.Name + ",";
                    else
                       if (!field.Sequence) strUpdateCols += field.FieldName + "=null,";
                    
                }
            }

            #region Select

            _sbSelectSQL.Append("SELECT * FROM " + _strTable);
            _sbSelectSQL.Append(_strWhereSQL);

            #endregion

            #region Insert

            strInsertCols = strInsertCols.Trim(',') + ")";
            strInsertValues = strInsertValues.Trim(',') + ")";

            _sbInsertSQL.Append(strInsertCols);

            if(!string.IsNullOrEmpty(strSet))
                _sbInsertSQL.Append(strInsertValues + ";SET @" + strSet + " = @@IDENTITY");
            else
                _sbInsertSQL.Append(strInsertValues);

            #endregion

            #region Update

            strUpdateCols = strUpdateCols.Trim(',');

            _sbUpdateSQL.Append(strUpdateCols);
            _sbUpdateSQL.Append(_strWhereSQL);

            #endregion

            #region Delete

            _sbDeleteSQL.Append(_strWhereSQL);

            #endregion
        }

        /// <summary>
        /// 转换为SqlDbType
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        private SqlDbType __Convert(object obj)
        {

            if (obj.GetType() == System.Type.GetType("System.Int16") ||
                obj.GetType() == System.Type.GetType("System.Int32") ||
                obj.GetType() == System.Type.GetType("System.UInt16") ||
                obj.GetType() == System.Type.GetType("System.UInt32"))
            {
                return System.Data.SqlDbType.Int;
            }
            else if (obj.GetType() == System.Type.GetType("System.Int64") ||
                obj.GetType() == System.Type.GetType("System.UInt64"))
            {
                return System.Data.SqlDbType.BigInt;
            }
            else if (obj.GetType() == System.Type.GetType("System.String") ||
                obj.GetType() == System.Type.GetType("System.Char") ||
                obj.GetType() == System.Type.GetType("System.TimeSpan"))
            {
                return System.Data.SqlDbType.VarChar;
            }
            else if (obj.GetType() == System.Type.GetType("System.Boolean") ||
                obj.GetType() == System.Type.GetType("System.Byte") ||
                obj.GetType() == System.Type.GetType("System.SByte"))
            {
                return System.Data.SqlDbType.Bit;
            }
            else if (obj.GetType() == System.Type.GetType("System.Decimal"))
            {
                return System.Data.SqlDbType.Decimal;
            }
            else if (obj.GetType() == System.Type.GetType("System.Double") ||
                obj.GetType() == System.Type.GetType("System.Single"))
            {

                return System.Data.SqlDbType.Float;

            }
            else if (obj.GetType() == System.Type.GetType("System.DateTime"))
            {
                return System.Data.SqlDbType.DateTime;
            }

            return SqlDbType.Variant;
        }


        /// <summary>
        /// 加到参数列表
        /// </summary>
        /// <param name="Name"></param>
        /// <param name="info"></param>
        private bool AddToParamsList(PropertyInfo info, GField field)
        {
            object obj = info.GetValue(_obj, null);

            if (obj != null)
            {

                SqlDbType dbtype = __Convert(obj);

                if (field.FieldType == FieldTypeEnum.PK)
                {
                    object kv = null;

                    if (_dicKeyValues.ContainsKey(info.Name))
                        kv = _dicKeyValues[info.Name];

                    _plKey.Add("@key_" + info.Name, dbtype, kv);
                    _pl.Add("@key_" + info.Name, dbtype, kv);


                    if (field.Sequence)
                    {
                        _pl.Add("@" + info.Name, dbtype, ParameterDirection.Output);
                        _plInsert.Add("@" + info.Name, dbtype, ParameterDirection.Output);
                    }
                    else
                    {
                        _plInsert.Add("@" + info.Name, dbtype, obj);
                        _pl.Add("@" + info.Name, dbtype, obj);
                    }
                    
                }
                else
                {
                    if (!field.Sequence)
                    {
                        _plInsert.Add("@" + info.Name, dbtype, obj);
                        _pl.Add("@" + info.Name, dbtype, obj);
                    }
                    else
                    {
                        _pl.Add("@" + info.Name, dbtype, ParameterDirection.Output);
                        _plInsert.Add("@" + info.Name, dbtype, ParameterDirection.Output);
                    }

                }



                return true;
            }

            return false;

        }

        #region 数据操作

        /// <summary>
        /// 保存到数据库
        /// 自动检查对象，如果存在则覆盖数据，不存在则插入新纪录
        /// </summary>
        /// <returns></returns>
        public virtual bool Save()
        {
            if (_rule.ShadowState == ShadowState.Deleted)
                return false;

            if (!string.IsNullOrEmpty(_strConnectStringName))
            {
                CreateSQLCommand();

                if (_rule.ShadowState == ShadowState.Auto)
                {

                    //检查数据库中该对象是否存在，存在则使用Update命令
                    object obj = null;

                    try{ obj = _sr.ExecuteScalarCommand("SELECT 1 FROM " + _strTable + _strWhereSQL, _strConnectStringName, _plKey); } catch { }

                    _sw.Begin(_strConnectStringName);

                    ParamList pl = _pl;

                    if (obj != null)    //数据库已包含该对象
                        _sw.RunCommandText(_sbUpdateSQL.ToString(), _pl);
                    else
                    {
                        _sw.RunCommandText(_sbInsertSQL.ToString(), _plInsert);
                        pl = _plInsert;
                    }

                    if (_sw.HasError)
                    {
                        _sw.RollBack();

                        _strErrorMessage = _sw.ErrorMessage;

                        return false;
                    }
                    else
                        _sw.Commit();


                    _RewriteKeyValues(pl);

                }
                else if (_rule.ShadowState == ShadowState.New)
                {
                    //插入新纪录

                    _sw.Begin(_strConnectStringName);

                    _sw.RunCommandText(_sbInsertSQL.ToString(), _plInsert);

                    if (_sw.HasError)
                    {
                        _sw.RollBack();

                        _strErrorMessage = _sw.ErrorMessage;

                        return false;
                    }
                    else
                        _sw.Commit();

                    _RewriteKeyValues(_plInsert);
                }
                else if (_rule.ShadowState == ShadowState.Edited)
                {
                    //编辑信息

                    _sw.Begin(_strConnectStringName);

                    _sw.RunCommandText(_sbUpdateSQL.ToString(), _pl);

                    if (_sw.HasError)
                    {
                        _sw.RollBack();

                        _strErrorMessage = _sw.ErrorMessage;

                        return false;
                    }
                    else
                        _sw.Commit();

                    _RewriteKeyValues(_pl);

                }

            }

            return true;
            //throw new NotImplementedException();
        }

        /// <summary>
        /// 删除操作
        /// </summary>
        /// <returns></returns>
        public virtual bool Delete()
        {
            _strErrorMessage = null;

            CreateSQLCommand();

            _sw.Begin(_strConnectStringName);

            _sw.RunCommandText(_sbDeleteSQL.ToString(), _plKey);

            if (_sw.HasError)
            {
                _sw.RollBack();

                _strErrorMessage = _sw.ErrorMessage;

                return false;
            }
            else
                _sw.Commit();

            _rule.ShadowState = ShadowState.Deleted;

            return true;
        }

        /// <summary>
        /// 获取对象
        /// </summary>
        /// <returns></returns>
        public virtual T GetItem()
        {
            CreateSQLCommand();

            DataTable dt = new DataTable();

            _sr.Fill(_sbSelectSQL.ToString(), _strConnectStringName, dt, _plKey);

            if (dt.Rows.Count > 0)
            {
                Type t = _obj.GetType();

                DataRow dr = dt.Rows[0];

                foreach (PropertyInfo pi in t.GetProperties())
                {
                    GField field = (pi.GetCustomAttributes(typeof(GField), false)[0] as GField);

                    if (field.FieldType != FieldTypeEnum.PK)
                    {
                        string strCol = field.FieldName;

                        strCol = strCol.Replace("[", "");
                        strCol = strCol.Replace("]", "");

                        if (dr[strCol] != DBNull.Value)
                            pi.SetValue(_obj, dr[strCol], null);
                    }
                }

                _rule.ShadowState = ShadowState.Edited;

                
            }
            else
                _rule.ShadowState = ShadowState.New;

            return _obj;

        }

        #endregion
    }
}
